package com.example.demo.widget.chart.broken

import android.content.Context
import android.graphics.*
import android.os.Build
import android.support.annotation.ColorInt
import android.support.annotation.RequiresApi
import android.util.AttributeSet
import android.view.View
import com.example.demo.R
import com.example.demo.widget.chart.utils.DrawTextUtils
import kotlin.math.ceil
import kotlin.math.cos
import kotlin.math.roundToInt
import kotlin.math.sin

/**
 * 折线统计图
 *
 * @author wangheng
 */
class ChartBrokenLineView : View {
    constructor(context: Context?) : super(context){
        initView(context,null)
    }
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs){
        initView(context,attrs)
    }
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ){
        initView(context,attrs)
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    constructor(
        context: Context?,
        attrs: AttributeSet?,
        defStyleAttr: Int,
        defStyleRes: Int
    ) : super(context, attrs, defStyleAttr, defStyleRes){
        initView(context,attrs)
    }

    // x轴线线的高度
    private var mXAxisSize = 3f
    // x轴线线的颜色
    private var mXAxisColor = Color.BLACK
    // x轴线刻度宽度
    private var mXAxisScaleWidth = 3f
    // x轴线刻度高度
    private var mXAxisScaleHeight = 10f
    // x轴线刻颜色
    private var mXAxisScaleColor = Color.BLACK

    // x轴刻度文案颜色
    private var mXAxisScaleTextColor = Color.BLACK
    // x轴刻度文案文字大小
    private var mXAxisScaleTextSize = 12f
    // x轴刻度文案和刻度的距离
    private var mXAxisScaleTextPadding = 10f


    // y轴刻度文案颜色
    private var mYAxisScaleTextColor = Color.BLACK
    // y轴刻度文案文字大小
    private var mYAxisScaleTextSize = 12f
    // y轴刻度文案和刻度的距离
    private var mYAxisScaleTextPadding = 10f

    // y轴刻度高度
    private var mYAxisScaleHeight = 60f

    // y轴刻度虚线线宽
    private var mYDashLineWidth = 10f
    // y轴刻度虚线空白宽度
    private var mYDashLineSpace = 5f
    // y轴刻度虚线高度
    private var mYDashLineHeight = 3f
    // y轴刻度虚线颜色
    private var mYDashLineColor = Color.BLACK

    // 圆点半径
    private var mPointRadius = 5f
    // 圆点颜色
    private var mPointColor = Color.BLACK

    // 圆点边缘大小
    private var mPointStrokeSize = 5f
    // 圆点边缘颜色
    private var mPointStrokeColor = Color.WHITE

    // 折线宽度
    private var mLineSize = 5f
    // 折线颜色
    private var mLineColor = Color.BLACK

    // 值字体大小
    private var mValueTextSize = 12f
    // 值字体颜色
    private var mValueTextColor = Color.BLACK
    // 值文字到圆圈的距离
    private var mValueTextPadding = 5f
    // 值文字是否加粗
    private var mValueTextBold = false


    private var mChartToLeft = 0f
    private var mChartToRight = 0f
    private var mChartToTop = 0f
    private var mChartToBottom = 0f

    private var mXAxisScaleTextAngle = 0f

    private val mLinePaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val mXAxisPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val mYAxisScaleTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val mXAxisScalePaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val mXAxisScaleTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val mYDashLinePaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val mValueTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val mPointPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val mPointStrokePaint = Paint(Paint.ANTI_ALIAS_FLAG)


    private fun initView(context: Context?, attrs: AttributeSet?) {
        if(context != null){
            val ta = context.obtainStyledAttributes(attrs, R.styleable.ChartBrokenLineView)

            // <!-- x轴线线的高度 -->
            mXAxisSize = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_x_axis_size,mXAxisSize)
            // <!-- x轴线线的颜色 -->
            mXAxisColor = ta.getColor(R.styleable.ChartBrokenLineView_b_line_chart_x_axis_color,mXAxisColor)
            // <!-- x轴线刻度宽度 -->
            mXAxisScaleWidth = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_x_axis_scale_width,mXAxisScaleWidth)
            // <!-- x轴线刻度高度 -->
            mXAxisScaleHeight = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_x_axis_scale_height,mXAxisScaleHeight)
            // <!-- x轴线刻颜色 -->
            mXAxisScaleColor = ta.getColor(R.styleable.ChartBrokenLineView_b_line_chart_x_axis_scale_color,mXAxisScaleColor)

            // <!-- x轴刻度文案颜色 -->
            mXAxisScaleTextColor = ta.getColor(R.styleable.ChartBrokenLineView_b_line_chart_x_axis_scale_text_color,mXAxisScaleTextColor)
            // <!-- x轴刻度文案文字大小 -->
            mXAxisScaleTextSize = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_x_axis_scale_text_size,mXAxisScaleTextSize)
            // <!-- x轴刻度文案和刻度的距离 -->
            mXAxisScaleTextPadding = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_x_axis_scale_text_padding,mXAxisScaleTextPadding)


            // <!-- y轴刻度文案颜色 -->
            mYAxisScaleTextColor = ta.getColor(R.styleable.ChartBrokenLineView_b_line_chart_y_axis_scale_text_color,mYAxisScaleTextColor)
            // <!-- y轴刻度文案文字大小 -->
            mYAxisScaleTextSize = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_y_axis_scale_text_size,mYAxisScaleTextSize)
            // <!-- y轴刻度文案和刻度的距离 -->
            mYAxisScaleTextPadding = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_y_axis_scale_text_padding,mYAxisScaleTextPadding)


            // <!-- y轴刻度高度 -->
            mYAxisScaleHeight = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_y_axis_scale_height,mYAxisScaleHeight)

            // <!-- y轴刻度虚线线宽 -->
            mYDashLineWidth = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_y_dash_line_width,mYDashLineWidth)
            // <!-- y轴刻度虚线空白宽度 -->
            mYDashLineSpace = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_y_dash_line_space,mYDashLineSpace)
            // <!-- y轴刻度虚线高度 -->
            mYDashLineHeight = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_y_dash_line_height,mYDashLineHeight)
            // <!-- y轴刻度虚线颜色 -->
            mYDashLineColor = ta.getColor(R.styleable.ChartBrokenLineView_b_line_chart_y_dash_line_color,mYDashLineColor)

            // <!-- 圆点半径 -->
            mPointRadius = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_point_radius,mPointRadius)
            // 圆点颜色
            mPointColor = ta.getColor(R.styleable.ChartBrokenLineView_b_line_chart_point_color,mPointColor)

            // <!-- 圆点边缘大小 -->
            mPointStrokeSize = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_point_stroke_size,mPointStrokeSize)
            // <!-- 圆点边缘颜色 -->
            mPointStrokeColor = ta.getColor(R.styleable.ChartBrokenLineView_b_line_chart_point_stroke_color,mPointStrokeColor)

            // <!-- 折线宽度 -->
            mLineSize = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_line_size,mLineSize)
            // <!-- 折线颜色 -->
            mLineColor = ta.getColor(R.styleable.ChartBrokenLineView_b_line_chart_line_color,mLineColor)


            // <!-- 值字体大小 -->
            mValueTextSize = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_value_text_size,mValueTextSize)
            // <!-- 值字体颜色 -->
            mValueTextColor = ta.getColor(R.styleable.ChartBrokenLineView_b_line_chart_value_text_color,mValueTextColor)
            // 值文字到圆点的距离
            mValueTextPadding = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_value_text_padding,mValueTextPadding)
            // 值文字是否加粗
            mValueTextBold = ta.getBoolean(R.styleable.ChartBrokenLineView_b_line_chart_value_text_bold,mValueTextBold)

            // app:b_line_chart_to_left 到左边的距离
            mChartToLeft = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_to_left,mChartToLeft)
            // app:b_line_chart_to_right 到右边的距离
            mChartToRight = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_to_right,mChartToRight)
            // app:b_line_chart_to_top 到上边的距离
            mChartToTop = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_to_top,mChartToTop)
            // app:b_line_chart_to_bottom 到下边的距离
            mChartToBottom = ta.getDimension(R.styleable.ChartBrokenLineView_b_line_chart_to_bottom,mChartToBottom)
            // app:b_line_chart_x_axis_scale_text_angle x轴刻度文案旋转角度
            mXAxisScaleTextAngle = ta.getFloat(R.styleable.ChartBrokenLineView_b_line_chart_x_axis_scale_text_angle,mXAxisScaleTextAngle)


            ta.recycle()
        }

        initLinePaint(mLinePaint,mLineSize,mLineColor)
        initLinePaint(mXAxisPaint,mXAxisSize,mXAxisColor)
        initLinePaint(mXAxisScalePaint,mXAxisScaleWidth,mXAxisScaleColor)
        initLinePaint(mYDashLinePaint,mYDashLineHeight,mYDashLineColor)

        initTextPaint(mXAxisScaleTextPaint,mXAxisScaleTextSize,mXAxisScaleTextColor)
        initTextPaint(mYAxisScaleTextPaint,mYAxisScaleTextSize,mYAxisScaleTextColor)
        initTextPaint(mValueTextPaint,mValueTextSize,mValueTextColor)
        mValueTextPaint.isFakeBoldText = mValueTextBold

        mPointPaint.style = Paint.Style.FILL
        mPointPaint.color = mPointColor

        mPointStrokePaint.style = Paint.Style.STROKE
        mPointStrokePaint.color = mPointStrokeColor
        mPointStrokePaint.strokeWidth = mPointStrokeSize
    }

    private fun initLinePaint(paint: Paint,size: Float,@ColorInt color: Int){
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = size
        paint.color = color
    }

    private fun initTextPaint(paint: Paint,textSize: Float,@ColorInt color: Int){
        paint.textSize = textSize
        paint.color = color
    }

    private var mData: ChartBrokenLineData? = null
    // y轴每个刻度的数值单位
    private var mYAxisScaleUnit = 0
    // Y轴刻度文案最大长度
    private var mYAxisScaleTextMaxLength = 0f
    // x轴刻度文字最大长度
    private var mXAxisScaleTextMaxLength = 0f
    // 当前y轴value最大值是否等于刻度最大值
    private var mYMaxValueSameScale = false

    fun setData(data: ChartBrokenLineData?){
        mData = data
        receiveDataInfo(data)
        requestLayout()
        postInvalidate()
    }

    private fun receiveDataInfo(data: ChartBrokenLineData?) {
        if (data == null) {
            return
        }
        val count = data.countOfYAxis
        if (count <= 0) {
            return
        }
        val maxValue = data.getMaxValue()
        val mode = maxValue % count
        val yAxisMaxScaleValue = if (mode == 0) {
            mYMaxValueSameScale = true
            maxValue
        } else {
            mYMaxValueSameScale = false
            maxValue + count - mode
        }
        mYAxisScaleUnit = yAxisMaxScaleValue / count
        mYAxisScaleTextMaxLength = mYAxisScaleTextPaint.measureText(yAxisMaxScaleValue.toString())

        // 得到名称最大长度
        val list = data.list
        if (list.isNotEmpty()) {
            list.forEach {
                val length = mXAxisScaleTextPaint.measureText(it.name)
                if(mXAxisScaleTextMaxLength < length){
                    mXAxisScaleTextMaxLength = length
                }
            }
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        if(widthMode == MeasureSpec.EXACTLY){
            val heightMode = MeasureSpec.EXACTLY
            // padding
            val padding = paddingTop + paddingBottom
            // y轴高度
            val count = mData?.countOfYAxis ?: 0
            val yAxisHeight = count * mYAxisScaleHeight
            val toTopBottom = mChartToTop + mChartToBottom
            // 文字旋转角度产生的大小
            val radian = Math.toRadians(mXAxisScaleTextAngle.toDouble())
            val sinValue = sin(radian)
            val maxTextHeight =  if(sinValue == 0.0){
                mXAxisScaleTextSize.toDouble()
            }else{
                mXAxisScaleTextMaxLength * sinValue
            }
            val deltaHeight = mXAxisScaleTextSize * cos(radian)

            val heightSize = ceil(padding + toTopBottom + yAxisHeight
                    + mXAxisScaleHeight + mXAxisScaleTextPadding +
                    maxTextHeight + deltaHeight + getMaxDeltaY()).roundToInt()
            val heightSpec = MeasureSpec.makeMeasureSpec(heightSize,heightMode)
            super.onMeasure(widthMeasureSpec, heightSpec)
        }else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        }

        receiveLocationInfo()
    }

    private fun getMaxDeltaY(): Float {

        // y轴刻度超出去y轴的高度
        val halfYAxisScaleTextSize = mYAxisScaleTextSize / 2f
        val valueTextPadding = mValueTextPadding + mValueTextSize
        return Math.max(valueTextPadding,halfYAxisScaleTextSize) + mPointStrokeSize / 2f
    }


    private val mXAxisStartPointF = PointF()
    private val mXAxisEndPointF = PointF()
    private val mYAxisStartPointF = PointF()
    private val mYAxisEndPointF = PointF()
    private var mYAxisScaleTextRect = RectF()

    private fun receiveLocationInfo() {
        // y轴刻度最大矩形
        mYAxisScaleTextRect.left = paddingLeft + mChartToLeft
        mYAxisScaleTextRect.right = mYAxisScaleTextRect.left + mYAxisScaleTextMaxLength

        // X轴开始点和X轴结束点坐标（其y轴坐标时一样的）
        mXAxisStartPointF.x = mYAxisScaleTextRect.right + mYAxisScaleTextPadding
        mXAxisStartPointF.y = paddingTop + mChartToTop + (mData?.countOfYAxis ?: 0) * mYAxisScaleHeight + getMaxDeltaY()
        mXAxisEndPointF.x = measuredWidth - paddingRight - mChartToRight
        mXAxisEndPointF.y = mXAxisStartPointF.y

        val data = mData
        val list = data?.list
        val scaleUnit = mYAxisScaleUnit
        // 修正x轴结束点x坐标(根据最后一个刻度绘制开始的偏移量:文字的大小，以及旋转后的大小以及每个刻度的宽度)
        if(data != null && list != null && list.size > 0 &&  data.countOfYAxis > 0){

            val lastName = list.last().name
            val drawWidth = mXAxisScaleTextPaint.measureText(lastName)
            val radian = Math.toRadians(mXAxisScaleTextAngle.toDouble())
            // 为什么加上刻度文字大小？因为刻度文字绘制的时候向右偏移了要给这个距离
            val xWidth = drawWidth * cos(radian) + mXAxisScaleTextSize
            val xUnit = (mXAxisEndPointF.x - mXAxisStartPointF.x) / list.size
            // 为什么除以2？因为每个刻度都是绘制在他所占的x轴宽度的中间位置的
            if(xWidth > xUnit / 2f) {
                mXAxisEndPointF.x = mXAxisEndPointF.x - (xWidth - xUnit / 2f).toFloat()
            }
        }

        // 计算并保存每个点的坐标
        if(data != null && list != null && data.countOfYAxis > 0 && scaleUnit > 0) {

            val perWidth = (mXAxisEndPointF.x - mXAxisStartPointF.x) / list.size
            val halfWidth = perWidth / 2.0f
            val xAxisFirstScaleX = mXAxisStartPointF.x + halfWidth      // x轴第一个刻度的x值
            val xAxisY = mXAxisStartPointF.y    // x轴的y值
            val totalHeight = data.countOfYAxis * mYAxisScaleHeight
            val maxValue = scaleUnit * data.countOfYAxis

            val size = list.size
            for(index in 0 until size){
                val item = list[index]
                item.position.x = xAxisFirstScaleX + index * perWidth
                item.position.y = xAxisY - totalHeight * (item.value.toFloat() / maxValue)
            }
        }
    }

    private val mYAxisScaleRectF = RectF()
    private val mXAxisScaleRectF = RectF()

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        val data = mData
        if(canvas == null || data == null){
            return
        }
        val list = data.list
        val yAxisCount = data.countOfYAxis
        if(list.isEmpty() || yAxisCount <= 0){
            return
        }

        drawDashLine(canvas,yAxisCount)
        drawYAxis(canvas,yAxisCount,list)
        drawXAxis(canvas,list)
        drawData(canvas,list)


    }

    // 绘制虚线
    private fun drawDashLine(canvas: Canvas,yAxisCount: Int) {
        var startX = mXAxisStartPointF.x
        val endX = mXAxisEndPointF.x
        val startY = mXAxisStartPointF.y
        while(startX + mYDashLineWidth < endX){
            for(index in 1..yAxisCount){
                val y = startY - index * mYAxisScaleHeight
                canvas.drawLine(startX,y,startX + mYDashLineWidth,y,mYDashLinePaint)
            }
            startX += (mYDashLineWidth + mYDashLineSpace)
        }

    }
    // 绘制Y轴刻度文字
    private fun drawYAxis(canvas: Canvas,yAxisCount: Int, list: ArrayList<ChartBrokenLinePoint>) {
        val left = mYAxisScaleTextRect.left
        val right = mYAxisScaleTextRect.right
        var top = mXAxisStartPointF.y - mYAxisScaleTextSize / 2f
        var bottom =  top + mYAxisScaleTextSize
        for(index in 0..yAxisCount){
            val text = (index * mYAxisScaleUnit).toString()
            DrawTextUtils.drawTextOnRectCenterRight(canvas,mYAxisScaleTextPaint,text,left,top,right,bottom)
            top -= mYAxisScaleHeight
            bottom = top + mYAxisScaleTextSize
        }
    }

    // 绘制X轴线、刻度、刻度文字
    private fun drawXAxis(canvas: Canvas, list: ArrayList<ChartBrokenLinePoint>) {
        canvas.drawLine(mXAxisStartPointF.x,mXAxisStartPointF.y,mXAxisEndPointF.x,mXAxisEndPointF.y,mXAxisPaint)
        val perWidth = (mXAxisEndPointF.x - mXAxisStartPointF.x) / list.size
        val halfWidth = perWidth / 2.0f
        var startScaleX = mXAxisStartPointF.x +  halfWidth
        val startScaleY = mXAxisStartPointF.y
        val endScaleY = mXAxisStartPointF.y + mXAxisScaleHeight
        val startScaleTextY = endScaleY + mXAxisScaleTextPadding
        val endScaleTextY = startScaleTextY + mXAxisScaleTextSize

        val halfTextSize = mXAxisScaleTextSize / 2f

        val size = list.size
        for(index in 0 until size){
            val item = list[index]
            canvas.drawLine(startScaleX,startScaleY,startScaleX,endScaleY,mXAxisScalePaint)
            canvas.save()
            canvas.rotate(mXAxisScaleTextAngle,startScaleX + halfTextSize,startScaleTextY)
            DrawTextUtils.drawTextOnRectCenterLeft(canvas,
                mXAxisScaleTextPaint,
                item.name,
                startScaleX + halfTextSize,
                startScaleTextY,
                startScaleX + halfTextSize + mXAxisScaleTextMaxLength,
                endScaleTextY)
            canvas.restore()
            startScaleX += perWidth
        }
    }
    // 绘制点、线、值
    private fun drawData(canvas: Canvas, list: ArrayList<ChartBrokenLinePoint>) {
        val size = list.size
        if(size == 0){
            return
        }
        for(index in 0 until size -1){
            val current = list[index]
            val next = list[index + 1]
            canvas.drawLine(current.position.x,current.position.y,next.position.x,next.position.y,mLinePaint)
        }

        val pointStrokeRadius = mPointRadius + mPointStrokeSize / 2f
        var textWidth = 0f
        var textLeft = 0f
        var textTop = 0f
        var text = ""
        list.forEach {
            canvas.drawCircle(it.position.x,it.position.y,mPointRadius,mPointPaint)
            canvas.drawCircle(it.position.x,it.position.y,pointStrokeRadius,mPointStrokePaint)

            text = it.value.toString()
            textWidth = mValueTextPaint.measureText(text)
            textLeft = it.position.x - textWidth / 2f
            textTop = it.position.y - mValueTextSize - mValueTextPadding - mPointRadius - mPointStrokeSize / 2f

            DrawTextUtils.drawTextOnRectCenter(canvas,mValueTextPaint, text,
                textLeft,
                textTop,
                textLeft + textWidth,
                textTop + mValueTextSize)
        }
    }
}